# -*- mode: makefile -*-
#
# Mongoose OS for Ubuntu

MAKEFLAGS += --warn-undefined-variables
.DEFAULT_GOAL := all

MGOS_PATH ?= ../..
VPATH = $(MGOS_VPATH) $(SRC_PATH) $(PLATFORM_VPATH)

APP ?= mongoose-os
APP_PLATFORM ?= ubuntu
APP_PATH := $(CURDIR)
SRC_PATH ?= ../../src
PLATFORM_VPATH ?= $(MGOS_PATH)/platforms/$(APP_PLATFORM)/src
BUILD_DIR ?= ./build
GEN_DIR = $(BUILD_DIR)/gen
FW_STAGING_DIR ?= $(BUILD_DIR)/fw_temp
FW_DIR ?= $(CURDIR)/fw
# Extra parts to include in firmware
APP_EXTRA_FW_PARTS ?=
FFI_SYMBOLS ?=

ASAN ?= 1
UBSAN ?= 1
PROF ?= 0

MGOS_ENABLE_DEBUG_UDP = 0
MGOS_ENABLE_SYS_SERVICE = 0

MGOS_POSIX_FEATURES ?= -DMGOS_PROMPT_DISABLE_ECHO -DMGOS_MAX_NUM_UARTS=2 \
                       -DMGOS_NUM_HW_TIMERS=0

MONGOOSE_FEATURES = \
  -DMG_USE_READ_WRITE -DMG_ENABLE_THREADS -DMG_ENABLE_THREADS \
  -DMG_ENABLE_HTTP_STREAMING_MULTIPART -DMG_DISABLE_DAV \
  -DMG_ENABLE_DIRECTORY_LISTING


GEN_INCLUDES ?= $(GEN_DIR)

APP_SOURCE_DIRS = $(sort $(dir $(APP_SOURCES)))
INCLUDES = $(MGOS_IPATH) $(SRC_PATH) $(BUILD_DIR) $(sort $(APP_SOURCE_DIRS) $(APP_INCLUDES)) $(GEN_INCLUDES) $(PLATFORM_VPATH)
MGOS_SRCS = mgos_init.c  \
            frozen.c mgos_event.c mgos_gpio.c \
            mgos_core_dump.c mgos_system.c mgos_system.cpp mgos_time.c \
            mgos_timers.c mgos_timers.cpp \
            mgos_config_util.c mgos_dlsym.c mgos_json_utils.cpp mgos_sys_config.c \
            json_utils.c cs_rbuf.c mgos_uart.c \
            mgos_utils.c mgos_utils.cpp cs_file.c cs_hex.c cs_crc32.c \
            error_codes.cpp status.cpp

PLATFORM_SRCS = $(wildcard $(PLATFORM_VPATH)/*.c) $(wildcard $(PLATFORM_VPATH)/*.cpp)

C_CXX_FLAGS = -O0 -ggdb -MD -Wall -Wextra -Werror -pipe \
              -ffunction-sections -fdata-sections \
              -DMGOS_APP=\"$(APP)\" \
              -DFW_ARCHITECTURE=$(APP_PLATFORM) \
              $(MGOS_FEATURES) $(MGOS_POSIX_FEATURES) \
              $(MONGOOSE_FEATURES)
LDFLAGS ?=

ifeq "$(ASAN)" "1"
CC = clang
CXX = clang++
C_CXX_FLAGS += -fsanitize=address -fno-omit-frame-pointer
LDFLAGS += -fsanitize=address
endif
ifeq "$(UBSAN)" "1"
CC = clang
CXX = clang++
C_CXX_FLAGS += -fsanitize=undefined -fno-omit-frame-pointer
LDFLAGS += -fsanitize=undefined
endif
ifeq "$(PROF)" "1"
CC = clang
CXX = clang++
C_CXX_FLAGS += -fprofile-instr-generate -fcoverage-mapping
LDFLAGS += -fprofile-instr-generate
endif

ifdef MGOS_HAVE_DNS_SD
LDFLAGS += -lavahi-client -lavahi-common -lstdc++
else ifneq "$(ASAN)" "1"
LDFLAGS += -static
endif

CFLAGS = -std=gnu99 $(C_CXX_FLAGS) $(APP_CFLAGS)
CXXFLAGS = -std=gnu++11 -fno-exceptions $(C_CXX_FLAGS) $(APP_CXXFLAGS)
LDFLAGS += -lstdc++

INCDIRS = $(addprefix -I,$(INCLUDES))
AR ?= ar
APP_BIN_LIBS ?=
LIBS  ?= pthread cap
LDLIBS = $(addprefix -l,$(LIBS))
BUILD_LIB_NAME ?= lib.a
APP_ELF = $(BUILD_DIR)/$(APP).elf

# Generated artifacts
BUILD_INFO_C = $(GEN_DIR)/build_info.c
BUILD_INFO_JSON = $(GEN_DIR)/build_info.json
MG_BUILD_INFO_C = $(GEN_DIR)/mg_build_info.c
MGOS_CONFIG_C = $(GEN_DIR)/mgos_config.c
MGOS_RO_VARS_C = $(GEN_DIR)/mgos_ro_vars.c
GENFILES_LIST = $(MGOS_CONFIG_C) $(MGOS_RO_VARS_C) $(BUILD_INFO_C) $(MG_BUILD_INFO_C)
GENFILES_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.c.o,$(GENFILES_LIST))
SYMBOLS_DUMP = $(GEN_DIR)/symbols_dump.txt
FFI_EXPORTS_C = $(GEN_DIR)/ffi_exports.c
FFI_EXPORTS_O = $(patsubst %.c,$(BUILD_DIR)/%.c.o,$(FFI_EXPORTS_C))

MGOS_CONF_SCHEMA += $(PLATFORM_VPATH)/ubuntu_sys_config.yaml

PLATFORM_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.c.o,$(filter %.c,$(PLATFORM_SRCS))) \
                $(patsubst %.cpp,$(BUILD_DIR)/%.cpp.o,$(filter %.cpp,$(PLATFORM_SRCS)))
MGOS_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.c.o,$(filter %.c,$(MGOS_SRCS))) \
            $(patsubst %.cpp,$(BUILD_DIR)/%.cpp.o,$(filter %.cpp,$(MGOS_SRCS)))
APP_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.c.o,$(filter %.c,$(APP_SOURCES))) \
           $(patsubst %.cpp,$(BUILD_DIR)/%.cpp.o,$(filter %.cpp,$(APP_SOURCES))) \
           $(FFI_EXPORTS_O)
OBJS = $(MGOS_OBJS) $(APP_OBJS) $(PLATFORM_OBJS) $(GENFILES_OBJS)

# Files
FS_STAGING_DIR ?= $(BUILD_DIR)/fs

FW_PARTS = app:type=app,src=$(APP_ELF)
FW_PARTS += $(APP_EXTRA_FW_PARTS)

.PHONY: all clean

include $(MGOS_PATH)/tools/mk/mgos_common.mk
include $(MGOS_PATH)/tools/mk/mgos_build_info.mk
include $(MGOS_PATH)/tools/mk/mgos_ffi_exports.mk
include $(MGOS_PATH)/tools/mk/mgos_fw_meta.mk
include $(MGOS_PATH)/tools/mk/mgos_config.mk
include $(MGOS_PATH)/tools/mk/mgos_features.mk
include $(MGOS_PATH)/tools/mk/mgos_mkfs.mk

all: $(BUILD_DIR) $(FW_STAGING_DIR) $(FW_DIR) $(GENFILES_LIST) $(OBJS) $(FS_STAGING_DIR) $(FW_ZIP) $(APP_ELF)

# Directories
$(BIN_DIR) $(BUILD_DIR) $(FW_STAGING_DIR) $(FW_DIR):
	$(vecho) MKDIR $@
	$(Q) mkdir -p $@

# Generated files
$(BUILD_INFO_C) $(BUILD_INFO_JSON):
	$(call gen_build_info,$@,$(APP_PATH),$(APP_BUILD_ID),$(APP_VERSION),,$(BUILD_INFO_C),$(BUILD_INFO_JSON))

$(MG_BUILD_INFO_C):
	$(call gen_build_info,$@,$(MGOS_PATH)/fw,,,mg_,$(MG_BUILD_INFO_C),)

# Output filesystem directory
$(FS_STAGING_DIR): $(FS_FILES)
	$(vecho) "MKDIR $@"
	$(Q) rm -rf $(@) && mkdir -p $@
	$(Q) $(foreach f,$(shell echo $(FS_FILES) | tr ' ' '\n' | sort | uniq), \
		echo "  CP    $(f) -> $(FS_STAGING_DIR)"; \
		cp $(f) $(FS_STAGING_DIR);)

$(FFI_EXPORTS_C): $(FS_FILES)
	$(call gen_ffi_exports,$@,$(FFI_SYMBOLS),$(filter %.js,$(FS_FILES)))

# In ffi exports file we use fake signatures: void func(void), and it conflicts
# with the builtin functions like fopen, etc.
$(FFI_EXPORTS_O): CFLAGS += -fno-builtin

# Objects of all sorts
$(BUILD_DIR)/%.c.o : %.c $(GENFILES_LIST)
	$(vecho) "CC    $<"
	$(Q) mkdir -p $(dir $@)
	$(Q) $(CC) $(CFLAGS) $(INCDIRS) -c $< -o $@

$(BUILD_DIR)/%.cpp.o : %.cpp
	$(vecho) "CXX   $<"
	$(Q) mkdir -p $(dir $@)
	$(Q) $(CXX) $(CXXFLAGS) $(INCDIRS) -c $< -o $@

# lib.a, used during lib release build
$(BUILD_DIR)/$(BUILD_LIB_NAME): $(BUILD_DIR) $(GENFILES_LIST) $(APP_OBJS)
	$(vecho) "AR    $@"
	$(Q) $(AR) cr $@ $(APP_OBJS)

# Application target.
$(APP_ELF): $(OBJS)
	$(vecho) "LD    $@"
	$(Q) $(CXX) -Wl,--gc-sections -Wl,-Map=$@.map \
	  -Wl,--start-group $(OBJS) $(APP_BIN_LIBS) $(LDLIBS) -Wl,--end-group \
	  $(LDFLAGS) -o $@

$(FW_MANIFEST): $(APP_ELF)

clean:
	$(Q) rm -rf $(BIN_DIR) $(BUILD_DIR)

# Slurp in auto-generated header dependencies.
-include $(patsubst %.o,%.d,$(OBJS))
